Write a custom CUDA kernel to optimize `Focal Tversky Loss` for 3D segmentation.

Formula: 
TI_c = (TP_c + epsilon) / (TP_c + alpha * FN_c + beta * FP_c + epsilon)
Loss = Mean( (1 - TI_c)^gamma )

Parameters:
- alpha, beta, gamma: Loss weighting parameters.
- epsilon: Smoothing factor for numerical stability.

Optimization Strategy: Fused Block-per-Channel Reduction

1. Flattened View: Treat the input (B, C, D, H, W) as `B * C` independent slices.

2. Block-per-Slice Parallelism: Launch `B * C` CUDA blocks. Each block reduces one spatial slice to compute TP, FN, and FP simultaneously.

3. Fused Accumulation: 
   - Load logit and target using vectorized `float4`.
   - Compute `p = sigmoid(logit)` in register.
   - Accumulate TP, FN, FP in registers.

4. Shared Memory Reduction: Perform parallel reduction for the three accumulators.

5. Final Calculation: Thread 0 uses the reduced TP, FN, FP and the passed `epsilon` to calculate the loss for the slice.

6. Global Reduction: The C++ wrapper performs the final mean reduction.
  
Here's an example to show you the syntax of inline embedding custom CUDA operators in torch: The example given architecture is:   
  
```python
import torch
import torch.nn as nn
import torch.nn.functional as F

BATCH_SIZE = 16
CHANNELS = 4
DEPTH = 32
HEIGHT = 128
WIDTH = 128
SHAPE = (BATCH_SIZE, CHANNELS, DEPTH, HEIGHT, WIDTH)

ALPHA = 0.7
BETA = 0.3
GAMMA = 0.75
EPSILON = 1e-7

class FocalTverskyLoss(nn.Module):
    '''
    FOCAL TVERSKY LOSS(https://arxiv.org/pdf/1810.07842)
    The input tensors are expected to have a shape of (B, N, H, W, L), where:
        B is the batch size
        N is the number of channels
        H, W, L represent the depth, height, and width of the volumes, respectively
    '''
    def __init__(self, alpha=0.7, beta=0.3, gamma=0.75, epsilon=1e-7):
        super(FocalTverskyLoss, self).__init__()
        self.alpha = alpha
        self.beta = beta
        self.gamma = gamma
        self.epsilon = epsilon

    def forward(self, y_pred, y_true):
        # y_pred: logits
        # y_true: binary targets (0 or 1)
        y_pred = torch.sigmoid(y_pred)
        
        # Reduction over spatial dims (2, 3, 4)
        tp = (y_true * y_pred).sum(dim=(2, 3, 4))
        fn = (y_true * (1 - y_pred)).sum(dim=(2, 3, 4))
        fp = ((1 - y_true) * y_pred).sum(dim=(2, 3, 4))
        
        tversky_index = (tp + self.epsilon) / (tp + self.alpha * fn + self.beta * fp + self.epsilon)
        
        loss = (1 - tversky_index).pow(self.gamma)
        
        return loss.mean()

class Model(nn.Module):
    def __init__(self, alpha=0.7, beta=0.3, gamma=0.75, epsilon=1e-7):
        super(Model, self).__init__()
        self.loss_fn = FocalTverskyLoss(alpha, beta, gamma, epsilon)
    
    def forward(self, y_pred, y_true):
        return self.loss_fn(y_pred, y_true)

def get_inputs():
    y_pred = torch.randn(SHAPE, dtype=torch.float32)
    y_true = torch.randint(0, 2, SHAPE, dtype=torch.float32)
    return [y_pred.contiguous(), y_true.contiguous()]

def get_init_inputs():
    return [ALPHA, BETA, GAMMA, EPSILON]